////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2008, 2009 The Mist project                                  //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the assertion system, used to enforce state properties. Behind the
/// scenes, it uses boost::assert, but changes it somewhat. It now throws an
/// AssertionFailure object upon failure, and assert(false) tells the compiler
/// that a location is unreachable, avoiding some warnings.
///

#ifndef ASSERT_HPP
#define ASSERT_HPP

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#define BOOST_ENABLE_ASSERT_HANDLER
#include <boost/assert.hpp>

#include <exception>
using std::exception;

#include <string>
using std::string;

#include <cstdlib>

////////////////////////////////////////////////////////////////////////////////
// assert Macro                                                               //
////////////////////////////////////////////////////////////////////////////////

#ifdef assert
	#undef assert
#endif

/// Assert \a expr to be true at the specified point. Use assert(false) to
/// specify unreachable code and the compiler will not give any control flow
/// warnings. There is no need to remove these calls after testing. They can
/// be 
///
#define assert(expr) (                                           \
	(expr) ? ((void)0) : (                                       \
		::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, \
		                          __FILE__, __LINE__),           \
		abort()                                                  \
	)                                                            \
)

////////////////////////////////////////////////////////////////////////////////
// AssertionFailure Exception Class                                           //
////////////////////////////////////////////////////////////////////////////////

/// Thrown when an assert call fails. Stores information about the nature and
/// location of the failure.
///
class AssertionFailure : public virtual exception {
	public:
		
		/// Construct an AssertionFailure object to throw.
		/// \internal
		///
		AssertionFailure(
			string expression, ///< the expression that failed to be true
			string function,   ///< the function in which the failure occurred
			string file,       ///< the file in which the failure occurred
			long line          ///< the line on which the failure occurred
		);
		
		~AssertionFailure() throw() {}
		
		/// \return a description of the assertion-failure in a C-string
		///
		const char* what() const throw();
		
		/// \return a description of the assertion-failure in an std::string
		///
		string what_str() const throw();
		
		/// \return the expression that failed to be true
		///
		const string& expression() const throw();
		
		/// \return the function in which the failure occurred
		///
		const string& function() const throw();
		
		/// \return the file in which the failure occurred
		///
		const string& file() const throw();
		
		/// \return the line on which the failure occurred
		///
		long line() const throw();
	
	private:
		
		string _expression; ///< the expression that failed to be true
		string _function;   ///< the function in which the failure occurred
		string _file;       ///< the file in which the failure occurred
		long   _line;       ///< the line on which the failure occurred
		
		mutable string _message;
};

////////////////////////////////////////////////////////////////////////////////
// Disabling assert Calls                                                     //
////////////////////////////////////////////////////////////////////////////////

/// \def BOOST_DISABLE_ASSERTS
/// If this macro is defined in the file assert.hpp, all assert calls in the
/// project are disabled.
///
/*/
#define BOOST_DISABLE_ASSERTS
//*/

#endif // ASSERT_HPP
